Add support for Qt6. (#739)
authortsteven4 <13596209+tsteven4@users.noreply.github.com>
Tue, 26 Oct 2021 19:49:48 +0000 (13:49 -0600)
committerGitHub <noreply@github.com>
Tue, 26 Oct 2021 19:49:48 +0000 (13:49 -0600)
* Add support for Qt6.

* fix a few codacy complaints.

27 files changed:
.github/workflows/macos.yml
.github/workflows/windows.yml
CMakeLists.txt
GPSBabel.pro
csv_util.cc
defs.h
garmin_tables.cc
gui/CMakeLists.txt
gui/app.pro
ignrando.cc
inifile.cc
lowranceusr.h
maggeo.cc
nmea.cc
pcx.cc
shape.cc
src/core/codecdevice.cc [new file with mode: 0644]
src/core/codecdevice.h [new file with mode: 0644]
src/core/logging.h
src/core/textstream.cc
src/core/textstream.h
tools/build_extra_tests
tools/build_qt6.sh [new file with mode: 0755]
tools/ci_install_qt.sh [new file with mode: 0755]
tools/travis_script_osx
util.cc
xcsv.cc

index 496397364dc93214604b457cefe24a5d0fb033e2..b814290faceaacd1f5e10cbeb992eb588d78b089 100644 (file)
@@ -85,3 +85,46 @@ jobs:
         source ${HOME}/Cache/qt-${QT_VERSION}.env
         sudo xcode-select --switch /Applications/Xcode_12.1.1.app
         ./tools/travis_script_osx
+
+  macos_qt6:
+    name: macos Qt6 Build
+    runs-on: macos-11
+    env:
+      QT_VERSION: '6.2.0'
+
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v2
+
+    - name: Cache Qt
+      uses: actions/cache@v2
+      id: cache
+      with:
+        path: ~/Cache
+        key: ${{ runner.os }}-${{ env.QT_VERSION }}-${{ secrets.CACHE_VERSION }}
+
+    - name: Qt install setup
+      if: steps.cache.outputs.cache-hit != 'true'
+      uses: actions/setup-python@v2
+      with:
+        python-version: '3.9'
+
+    - name: Qt install
+      if: steps.cache.outputs.cache-hit != 'true'
+      run: |
+        pip3 install aqtinstall>=2.0.0
+        ./tools/ci_install_qt.sh mac ${QT_VERSION} clang_64 ${HOME}/Cache/Qt
+        echo "export PATH=${HOME}/Cache/Qt/${QT_VERSION}/macos/bin:\$PATH" > "${HOME}/Cache/qt-${QT_VERSION}.env"
+
+    - name: Script
+      run: |
+        source ${HOME}/Cache/qt-${QT_VERSION}.env
+        sudo xcode-select --switch /Applications/Xcode_12.5.1.app
+        ./tools/travis_script_osx
+
+    - name: 'Upload Artifacts'
+      uses: actions/upload-artifact@v2
+      with:
+        name: MacOS_Installer_Qt6
+        path: gui/GPSBabel-*.dmg
+        retention-days: 14
index 59c59811b8bfafb980f80a1073217fd855f97e7f..fa7387c04fa925a7a4ad253889b5708d90a5b7dd 100644 (file)
@@ -49,12 +49,13 @@ jobs:
             COMPILER: 'msvc2017'
             RELEASE: false
             FLOW: 'nmake'
-          - QT_VERSION: '5.12.10'
-            ARCH: 'x86'
-            HOST_ARCH: 'x86'
-            COMPILER: 'msvc2017'
+          - QT_VERSION: '6.2.0'
+            ARCH: 'amd64'
+            HOST_ARCH: 'amd64'
+            COMPILER: 'msvc2019_64'
+            AQTARCH: 'win64_msvc2019_64'
             RELEASE: false
-            FLOW: 'msbuild'
+            FLOW: 'nmake'
 
     steps:
     - name: Checkout repository
@@ -62,11 +63,26 @@ jobs:
 
     - name: Cache Qt
       uses: actions/cache@v2
+      id: cache
       with:
         path: ~/Cache
         key: ${{ runner.os }}-${{ matrix.QT_VERSION }}-${{ matrix.COMPILER }}-${{ secrets.CACHE_VERSION }}
 
+    - name: Install Qt setup(aqt)
+      if: ${{ (steps.cache.outputs.cache-hit != 'true') && startsWith(matrix.QT_VERSION, '6') }}
+      uses: actions/setup-python@v2
+      with:
+        python-version: '3.9'
+
+    - name: Install Qt(aqt)
+      if: ${{ (steps.cache.outputs.cache-hit != 'true') && startsWith(matrix.QT_VERSION, '6') }}
+      shell: bash
+      run: |
+        pip3 install aqtinstall>=2.0.0
+        ./tools/ci_install_qt.sh windows ${{ matrix.QT_VERSION }} ${{ matrix.AQTARCH }} ${HOME}/Cache/Qt
+
     - name: Install Qt
+      if: ${{ (steps.cache.outputs.cache-hit != 'true') && !startsWith(matrix.QT_VERSION, '6') }}
       env:
         CI_BUILD_DIR: ${{ github.workspace }}
       shell: bash
index 65358f70279922a9690e6a5e40b4414be0aff25d..cfaf4a65cbb3a8ba7c2ebf4ce515e991b7c7709a 100644 (file)
@@ -13,12 +13,18 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
 # Find includes in corresponding build directories
 set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
-# Find the Qt5Core library
-find_package(Qt5 COMPONENTS Core REQUIRED)
-if(${Qt5Core_VERSION} VERSION_LESS 5.12)
-  message(FATAL_ERROR "Qt version ${Qt5Core_VERSION} found, but version 5.9 or newer is required.")
+# Find the QtCore library
+find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
+find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED)
+list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::Core)
+if(${Qt${QT_VERSION_MAJOR}Core_VERSION} VERSION_LESS 5.12)
+  message(FATAL_ERROR "Qt version ${Qt${QT_VERSION_MAJOR}Core_VERSION} found, but version 5.12 or newer is required.")
 else()
-  message(STATUS "Using Qt5 version ${Qt5Core_VERSION}")
+  message(STATUS "Using Qt${QT_VERSION_MAJOR} version ${Qt${QT_VERSION_MAJOR}Core_VERSION}")
+endif()
+if(${QT_VERSION_MAJOR} EQUAL "6")
+  find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core5Compat REQUIRED)
+  list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::Core5Compat)
 endif()
 
 set(MINIMAL_FMTS
@@ -94,6 +100,9 @@ set(SUPPORT
   src/core/usasciicodec.cc
   src/core/xmlstreamwriter.cc
 )
+if(${QT_VERSION_MAJOR} EQUAL "6")
+  set(SUPPORT ${SUPPORT} src/core/codecdevice.cc)
+endif()
 
 set(HEADERS
   cet.h
@@ -183,6 +192,9 @@ set(HEADERS
   src/core/xmlstreamwriter.h
   src/core/xmltag.h
 )
+if(${QT_VERSION_MAJOR} EQUAL "6")
+  set(HEADERS ${HEADERS} src/core/codecdevice.h)
+endif()
 
 string(REPLACE .cc .h FILTER_HEADERS "${FILTERS}")
 set(HEADERS ${HEADERS} ${FILTER_HEADERS})
@@ -268,7 +280,7 @@ add_definitions(-DSHAPELIB_ENABLED)
 add_definitions(-DCSVFMTS_ENABLED)
 
 add_executable(gpsbabel ${SOURCES} ${HEADERS})
-target_link_libraries(gpsbabel Qt5::Core ${LIBS})
+target_link_libraries(gpsbabel ${QT_LIBRARIES} ${LIBS})
 
 message(STATUS "Sources are: \"${SOURCES}\"")
 message(STATUS "Headers are: \"${HEADERS}\"")
index 5627a763195b9b8c71a2f2918e777010b9d9dc05..1bbb8d0cfdec9a875bb6ef372ec317afe49b92e1 100644 (file)
@@ -14,6 +14,7 @@ if(equals(QT_MAJOR_VERSION, $$MIN_QT_VERSION_MAJOR):equals(QT_MINOR_VERSION, $$M
 }
 
 QT -= gui
+versionAtLeast(QT_VERSION, 6.0): QT += core5compat
 
 TARGET = gpsbabel
 VERSION = 1.7.0
@@ -110,6 +111,8 @@ SUPPORT = route.cc waypt.cc filter_vecs.cc util.cc vecs.cc mkshort.cc \
           src/core/usasciicodec.cc \
           src/core/xmlstreamwriter.cc
 
+versionAtLeast(QT_VERSION, 6.0): SUPPORT += src/core/codecdevice.cc
+
 HEADERS =  \
        cet.h \
        cet_util.h \
@@ -186,6 +189,8 @@ HEADERS =  \
        src/core/xmlstreamwriter.h \
        src/core/xmltag.h
 
+versionAtLeast(QT_VERSION, 6.0): HEADERS += src/core/codecdevice.h
+
 HEADERS += $$FILTER_HEADERS
 
 win32-msvc* {
index 33128e756d01588cc47e2b6ad67a09ceea58bed3..8233533b8b177e3de027f67c745c4a457913b65f 100644 (file)
@@ -31,7 +31,6 @@
 #include <QDebug>              // for QDebug
 #include <QRegularExpression>  // for QRegularExpression
 #include <QString>             // for QString, operator+
-#include <QStringRef>          // for QStringRef
 
 #include "defs.h"
 #include "csv_util.h"
@@ -372,7 +371,7 @@ csv_linesplit(const QString& string, const QString& delimited_by,
     const int sp = p;
 
     while (p < string.size() && !dfound) {
-      if ((elen > 0) && string.midRef(p).startsWith(enclosed_in)) {
+      if ((elen > 0) && string.mid(p).startsWith(enclosed_in)) {
         efound = true;
         p += elen;
         enclosed = !enclosed;
@@ -380,7 +379,7 @@ csv_linesplit(const QString& string, const QString& delimited_by,
       }
 
       if (!enclosed) {
-        if ((dlen > 0) && string.midRef(p).startsWith(delimiter)) {
+        if ((dlen > 0) && string.mid(p).startsWith(delimiter)) {
           dfound = true;
         } else if (hyper_whitespace_delimiter && string.at(p).isSpace()) {
           dfound = true;
diff --git a/defs.h b/defs.h
index a9c43b29bf4a37b919415458c17eefdbd4826adb..70ee1262abe0cd1f7fabf65b6cc722411dd4b45d 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -580,6 +580,7 @@ public:
   using QList<Waypoint*>::count; // a.k.a. size()
   using QList<Waypoint*>::crbegin;
   using QList<Waypoint*>::crend;
+  using QList<Waypoint*>::detach; // silence Qt6 foreach warnings
   using QList<Waypoint*>::empty; // a.k.a. isEmpty()
   using QList<Waypoint*>::end;
   using QList<Waypoint*>::front; // a.k.a. first()
@@ -758,6 +759,7 @@ public:
   using QList<route_head*>::count; // a.k.a. size()
   using QList<route_head*>::crbegin;
   using QList<route_head*>::crend;
+  using QList<route_head*>::detach; // silence Qt6 foreach warnings
   using QList<route_head*>::empty; // a.k.a. isEmpty()
   using QList<route_head*>::end;
   using QList<route_head*>::front; // a.k.a. first()
@@ -1080,7 +1082,7 @@ case_ignore_strcmp(const QString& s1, const QString& s2)
 // In 95% of the callers, this could be s1.startsWith(s2)...
 inline int case_ignore_strncmp(const QString& s1, const QString& s2, int n)
 {
-  return s1.leftRef(n).compare(s2.left(n), Qt::CaseInsensitive);
+  return s1.left(n).compare(s2.left(n), Qt::CaseInsensitive);
 }
 
 int str_match(const char* str, const char* match);
index 3220f20bc05f19347b7fdf9240b80d196e82baa4..a0f1c3e97695c76b8994f6e43619b391d6dfe765 100644 (file)
@@ -480,7 +480,7 @@ int gt_find_icon_number_from_desc(const QString& desc, garmin_formats_e garmin_f
       base = 7680;
     }
     if (base) {
-      n = desc.midRef(7).toInt();
+      n = desc.mid(7).toInt();
       return n + base;
     }
   }
index aaf5548cf5918d3442704b0ba8343804f210c0a6..3d0c77b0e62242f277c9e12b26ce9fdef03fd1b0 100644 (file)
@@ -19,18 +19,18 @@ set(CMAKE_AUTOUIC ON)
 # Handle the Qt rcc code generator automatically
 set(CMAKE_AUTORCC ON)
 
-# Find the Qt5Core library
-find_package(Qt5 COMPONENTS Core Gui Network Widgets Xml REQUIRED)
-list(APPEND QT_LIBRARIES Qt5::Core Qt5::Gui Qt5::Network Qt5::Widgets Qt5::Xml)
-if(${Qt5Core_VERSION} VERSION_LESS 5.12)
-  message(FATAL_ERROR "Qt version ${Qt5Core_VERSION} found, but version 5.9 or newer is required.")
+# Find the QtCore library
+find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
+find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network Widgets Xml REQUIRED)
+list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Xml)
+if(${Qt${QT_VERSION_MAJOR}Core_VERSION} VERSION_LESS 5.12)
+  message(FATAL_ERROR "Qt version ${Qt${QT_VERSION_MAJOR}Core_VERSION} found, but version 5.12 or newer is required.")
 else()
-  message(STATUS "Using Qt5 version ${Qt5Core_VERSION}")
+  message(STATUS "Using Qt${QT_VERSION_MAJOR} version ${Qt${QT_VERSION_MAJOR}Core_VERSION}")
 endif()
 
-# hard code webengine instead of webkit for now
-find_package(Qt5 COMPONENTS WebEngineWidgets WebChannel REQUIRED)
-list(APPEND QT_LIBRARIES Qt5::WebEngineWidgets Qt5::WebChannel)
+find_package(Qt${QT_VERSION_MAJOR} COMPONENTS WebEngineWidgets WebChannel REQUIRED)
+list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::WebEngineWidgets Qt${QT_VERSION_MAJOR}::WebChannel)
 
 if(APPLE)
   find_library(IOKIT_LIBRARIES IOKit)
index 2746f05cec7e007ea8562e6bdd07f03144f3cafa..66e804d6245142f565446c1d9a2874d9978c0958 100755 (executable)
@@ -1,7 +1,7 @@
 # $Id: app.pro,v 1.19 2010-11-01 03:30:42 robertl Exp $
 #
 
-CONFIG += qt
+#CONFIG += qt causes link failure on msvc.  Qt6EntryPoint.lib not added to libs.
 CONFIG(debug, debug|release) {
   CONFIG += console
 }
index f5e5b385527aea2fc379ebc27af8a7f85cc64a03..fdda2e7bd39637216cdaf452b319be14f1db0699 100644 (file)
        Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
 
+
+#include <cstdio>                  // for sscanf
+#include <cstdlib>                 // for atoi
+#include <ctime>                   // for strftime, localtime, time_t, tm
+
+#include <QByteArray>              // for QByteArray
+#include <QIODevice>               // for QIODevice, QIODeviceBase::ReadOnly
+#include <QList>                   // for QList
+#include <QString>                 // for QString, operator==
+#include <QXmlStreamAttributes>    // for QXmlStreamAttributes
+#include <QtGlobal>                // for QT_VERSION, QT_VERSION_CHECK, qPrintable
+
 #include "defs.h"
-#include "xmlgeneric.h"
-#include <QXmlStreamAttributes>
-#include <cstdio>
+#include "gbfile.h"                // for gbfprintf, gbfclose, gbfopen, gbfile
+#include "src/core/datetime.h"     // for DateTime
+#include "src/core/file.h"         // for File
+#include "xmlgeneric.h"            // for xg_callback, xg_string, cb_cdata, xml_deinit, xml_init, xml_readunicode, cb_start, cb_end, xg_cb_type, xg_tag_mapping
+
 
 #define MYNAME "IGNRando"
 
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+static QString rd_fname;
+#endif
 static gbfile* fout;
 
 static route_head* track;
@@ -114,7 +131,7 @@ ignr_etape_pos(xg_string args, const QXmlStreamAttributes*)
 {
   ignr_xml_error((wpt == nullptr) || (args.isEmpty()));
 
-  if (2 != sscanf(CSTRc(args), "%lf,%lf", &wpt->latitude, &wpt->longitude)) {
+  if (2 != sscanf(STRFROMUNICODE(args), "%lf,%lf", &wpt->latitude, &wpt->longitude)) {
     fatal(MYNAME ": Invalid coordinates \"%s\"!\n", qPrintable(args));
   }
 }
@@ -127,7 +144,7 @@ ignr_etape_alt(xg_string args, const QXmlStreamAttributes*)
     return;
   }
 
-  if (1 != sscanf(CSTRc(args), "%lf", &wpt->altitude)) {
+  if (1 != sscanf(STRFROMUNICODE(args), "%lf", &wpt->altitude)) {
     fatal(MYNAME ": Invalid altitude \"%s\"!\n", qPrintable(args));
   }
 }
@@ -137,7 +154,12 @@ ignr_etape_alt(xg_string args, const QXmlStreamAttributes*)
 static void
 ignr_rd_init(const QString& fname)
 {
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
   xml_init(fname, ignr_xml_map, nullptr);
+#else
+  rd_fname = fname;
+  xml_init(nullptr, ignr_xml_map, nullptr);
+#endif
   wpt = nullptr;
   track = nullptr;
 }
@@ -146,12 +168,29 @@ static void
 ignr_rd_deinit()
 {
   xml_deinit();
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+  rd_fname.clear();
+#endif
 }
 
 static void
 ignr_read()
 {
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
+  // QXmlStreamReader had access to the windows-1252 QTextCodec that we expect
+  // to find in the XMLDecl.
   xml_read();
+#else
+  // QXmlStreamReader doesn't have access to a windows-1252 QStringDecoder,
+  // and will throw an error if we pass a QIODevice when it sees windows-1252
+  // in the XMLDecl.
+  // Therfore we must decode the input manually and pass a QString.
+  // With a QString QXmlStreamReader will ignore the XMLDecl.
+  gpsbabel::File file(rd_fname);
+  file.open(QIODevice::ReadOnly);
+  xml_readunicode(STRTOUNICODE(file.readAll()));
+  file.close();
+#endif
 }
 
 /* write support */
@@ -182,7 +221,7 @@ ignr_write_track_hdr(const route_head* track_hdr)
   gbfprintf(fout, "\t<INFORMATIONS>\n");
   gbfprintf(fout, "\t\t<NB_ETAPES>%d</NB_ETAPES>\n", track_hdr->rte_waypt_ct);
   if (!track_hdr->rte_desc.isEmpty()) {
-    gbfprintf(fout, "\t\t<DESCRIPTION>%s</DESCRIPTION>\n", CSTRc(track_hdr->rte_desc));
+    gbfprintf(fout, "\t\t<DESCRIPTION>%s</DESCRIPTION>\n", STRFROMUNICODE(track_hdr->rte_desc));
   }
   gbfprintf(fout, "\t</INFORMATIONS>\n");
 }
index 4f22fdce3ce095d5199932c207f02c3c5a9b988a..404891cd1b861766196e50b048a885e430ff51e1 100644 (file)
@@ -185,7 +185,11 @@ inifile_init(const QString& filename, const char* myname)
   gpsbabel::File file(name);
   file.open(QFile::ReadOnly);
   QTextStream stream(&file);
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
+  // default for QTextStream::setCodec in Qt5 is QTextCodec::codecForLocale()
+  // default for QTextStream::setEncoding in Qt6 is QStringConverter::Utf8
   stream.setCodec("UTF-8");
+#endif
   stream.setAutoDetectUnicode(true);
 
   auto* result = new inifile_t;
index ce082b7207ffb607906a3647199d3f916a840af1..249774c01eee493efc90c0480861c762fde8a82a 100644 (file)
@@ -500,7 +500,7 @@ private:
      * Also return the icon number for descriptions of "icon-"
      * followed by a numeric icon number.
      */
-    int n = desc.midRef(desc.startsWith("icon-") ? 5 : 0).toInt();
+    int n = desc.mid(desc.startsWith("icon-") ? 5 : 0).toInt();
     if (n)  {
       return n;
     }
index f1b199ccdc45408d865e2a2f8f63198d75cc15cf..5ede9ef202f16a6f5f13a6c13fdc95acc5a7f7ac 100644 (file)
--- a/maggeo.cc
+++ b/maggeo.cc
@@ -191,10 +191,14 @@ maggeo_fmtdate(const QDateTime& dt)
 static QDateTime maggeo_parsedate(char* dmy)
 {
   QString date(dmy);
-  int d = date.midRef(0,2).toInt();
-  int m = date.midRef(2,2).toInt();
-  int y = date.midRef(4,3).toInt();
+  int d = date.mid(0,2).toInt();
+  int m = date.mid(2,2).toInt();
+  int y = date.mid(4,3).toInt();
+#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
   QDateTime r(QDate(y + 1900, m, d));
+#else
+  QDateTime r = QDate(y + 1900, m, d).startOfDay();
+#endif
   return r;
 }
 
diff --git a/nmea.cc b/nmea.cc
index 733e9967ba61d03c5f440e77f03f7f7220bff7c5..8c55f9b93cb6b66fe64d7361e5c283b65ba4ccf0 100644 (file)
--- a/nmea.cc
+++ b/nmea.cc
@@ -876,7 +876,7 @@ NmeaFormat::nmea_parse_one_line(const QByteArray& ibuf)
       if (ok) {
         int ckval = nmea_cksum(tbuf.mid(1, ckidx - 1));
         if (ckval != ckcmp) {
-          Warning().nospace() <<  hex << "Invalid NMEA checksum.  Computed 0x" << ckval << " but found 0x" << ckcmp << ".  Ignoring sentence.";
+          Warning().nospace() <<  Qt::hex << "Invalid NMEA checksum.  Computed 0x" << ckval << " but found 0x" << ckcmp << ".  Ignoring sentence.";
           return;
         }
         checked = true;
diff --git a/pcx.cc b/pcx.cc
index 30fd51b6d434808d74e86c3bc0ac5217d4fb2e61..7a50015d71814c4eb9867bbe733617865afb6837 100644 (file)
--- a/pcx.cc
+++ b/pcx.cc
@@ -182,8 +182,8 @@ static void data_read() {
           wpt_tmp->longitude = lon;
           wpt_tmp->latitude = lat;
         } else {
-          lat = tbuf.midRef(1, -1).toDouble();
-          lon = nbuf.midRef(1, -1).toDouble();
+          lat = tbuf.mid(1, -1).toDouble();
+          lon = nbuf.mid(1, -1).toDouble();
           if (tbuf[0] == 'S') {
             lat = -lat;
           }
@@ -254,8 +254,8 @@ static void data_read() {
           wpt_tmp->longitude = lon;
           wpt_tmp->latitude = lat;
         } else {
-          lat = tbuf.midRef(1, -1).toDouble();
-          lon = nbuf.midRef(1, -1).toDouble();
+          lat = tbuf.mid(1, -1).toDouble();
+          lon = nbuf.mid(1, -1).toDouble();
           if (tbuf[0] == 'S') {
             lat = -lat;
           }
index 6a11e022a60dc66aabb83776ee39f893d80842e4..6bb81a4996c38c4442b010c2f6f2b48f9f3bedda 100644 (file)
--- a/shape.cc
+++ b/shape.cc
@@ -24,7 +24,7 @@
 
 #include <QByteArray>            // for QByteArray
 #include <QLatin1String>         // for QLatin1String
-#include <QString>               // for QString, QString::SkipEmptyParts
+#include <QString>               // for QString, Qt::SkipEmptyParts
 #include <QStringList>           // for QStringList
 #include <QVector>               // for QVector
 #include <Qt>                    // for CaseInsensitive
@@ -218,7 +218,11 @@ ShapeFormat::read()
     if (qopt_name.contains('+')) {
       // form a compound name from one or more fields.
       nameidx = -2;
+#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
       const QStringList opt_name_fields = qopt_name.split('+', QString::SkipEmptyParts);
+#else
+      const QStringList opt_name_fields = qopt_name.split('+', Qt::SkipEmptyParts);
+#endif
       nameindices.reserve(opt_name_fields.size());
       for (int oidx=0; oidx<opt_name_fields.size(); oidx++) {
         bool ok;
diff --git a/src/core/codecdevice.cc b/src/core/codecdevice.cc
new file mode 100644 (file)
index 0000000..13290d0
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+    Copyright (C) 2021 Robert Lipe, robertlipe+source@gpsbabel.org
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+ */
+
+#include <cstring>     // for memcpy
+#include <algorithm>   // for min
+
+#include <QByteArray>  // for QByteArray
+#include <QChar>       // for QChar
+#include <QFile>       // for QFile
+#include <QFlags>      // for QFlags
+
+#include "defs.h"      // for list_codecs, warning
+#include "codecdevice.h"
+
+namespace gpsbabel
+{
+
+CodecDevice::CodecDevice(const QString& fname, const char* module, const char* codec_name) :
+  fname_(fname), module_(module), codec_name_(codec_name)
+{
+}
+
+CodecDevice::~CodecDevice()
+{
+//  close();
+}
+
+bool CodecDevice::open(QIODevice::OpenMode mode)
+{
+  codec_ = QTextCodec::codecForName(codec_name_);
+  if (codec_ == nullptr) {
+    list_codecs();
+    fatal("%s: Unsupported codec '%s'.\n", module_, codec_name_);
+    // return false;
+  }
+
+  if (mode & QIODevice::ReadOnly) {
+    decoder_ = codec_->makeDecoder();
+  }
+
+  if (mode & QIODevice::WriteOnly) {
+    encoder_ = codec_->makeEncoder();
+  }
+
+  file_ = new gpsbabel::File(fname_);
+  bool status = file_->open(mode);
+
+  QIODevice::open(mode);
+
+  return status;
+
+}
+
+qint64 CodecDevice::readData(char* data, qint64 maxlen)
+{
+  qint64 bytesdelivered = 0;
+
+  while (bytesdelivered < maxlen) {
+    if (unicodebuffer_bytes_ > 0) {
+      qint64 bytes = std::min(maxlen, unicodebuffer_bytes_);
+      memcpy(data, unicodebuffer_data_, bytes);
+      unicodebuffer_bytes_ -= bytes;
+      unicodebuffer_data_ += bytes;
+      data += bytes;
+      bytesdelivered += bytes;
+      if (bytesdelivered == maxlen) {
+        break;
+      }
+    }
+
+    qint64 bytesread = file_->read(charbuffer_, charbuffer_size_);
+    if (bytesread <= 0) { // no more data is available or error.
+      if (bytesdelivered > 0) {
+        break;
+      }
+      return -1;
+    }
+
+    unicodebuffer_ = decoder_->toUnicode(charbuffer_, bytesread);
+    unicodebuffer_bytes_ = unicodebuffer_.size() * sizeof(QChar);
+    unicodebuffer_data_ = reinterpret_cast<const char*>(unicodebuffer_.constData());
+  }
+  return bytesdelivered;
+}
+
+qint64 CodecDevice::writeData(const char* data, qint64 len)
+{
+  qint64 bytes_consumed = 0;
+
+  while (bytes_consumed < len) {
+    qint64 bytes= std::min(len - bytes_consumed, charbuffer_bytes_free_);
+    memcpy(charbuffer_data_, data, bytes);
+    bytes_consumed += bytes;
+    charbuffer_data_ += bytes;
+    charbuffer_bytes_free_ -= bytes;
+    data += bytes;
+
+    if (charbuffer_bytes_free_ == 0) {
+      static_assert(charbuffer_size_%sizeof(QChar) == 0);
+      QByteArray ba = encoder_->fromUnicode(reinterpret_cast<const QChar*>(charbuffer_), charbuffer_size_/sizeof(QChar));
+      file_->write(ba);
+      charbuffer_data_ = charbuffer_;
+      charbuffer_bytes_free_ = charbuffer_size_;
+    }
+  }
+  return len;
+}
+
+void CodecDevice::close()
+{
+  if (charbuffer_bytes_free_ < charbuffer_size_) {
+    qint64 bytes = charbuffer_size_ - charbuffer_bytes_free_;
+    assert(bytes%sizeof(QChar) == 0);
+    QByteArray ba = encoder_->fromUnicode(reinterpret_cast<const QChar*>(charbuffer_), bytes/sizeof(QChar));
+    file_->write(ba);
+    charbuffer_data_ = charbuffer_;
+    charbuffer_bytes_free_ = charbuffer_size_;
+  }
+  file_->close();
+  QIODevice::close();
+
+  if (file_ != nullptr) {
+    delete file_;
+    file_ = nullptr;
+  }
+  if (encoder_ != nullptr) {
+    delete encoder_;
+    encoder_ = nullptr;
+  }
+  if (decoder_ != nullptr) {
+    delete decoder_;
+    decoder_ = nullptr;
+  }
+}
+
+bool CodecDevice::isSequential() const
+{
+  return true;
+}
+
+} // namespace
diff --git a/src/core/codecdevice.h b/src/core/codecdevice.h
new file mode 100644 (file)
index 0000000..320d9aa
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+    Copyright (C) 2021 Robert Lipe, robertlipe+source@gpsbabel.org
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+ */
+
+#include <QIODevice>        // for QIODevice
+#include <QString>          // for QString
+#include <QTextCodec>       // for QTextCodec
+#include <QTextDecoder>     // for QTextDecoder
+#include <QTextEncoder>     // for QTextEncoder
+#include <QtCore>           // for qint64, QIODeviceBase::OpenMode
+
+#include "src/core/file.h"  // for File
+
+namespace gpsbabel
+{
+
+class CodecDevice : public QIODevice
+{
+public:
+  CodecDevice(const QString& fname, const char* module, const char* codec_name);
+  ~CodecDevice();
+  bool open(QIODevice::OpenMode mode) override;
+  bool isSequential() const override;
+  void close() override;
+
+private:
+  qint64 readData(char* data, qint64 maxlen) override;
+  qint64 writeData(const char* data, qint64 len) override;
+
+private:
+  QString fname_;
+  const char* module_;
+  const char* codec_name_;
+  gpsbabel::File* file_{nullptr};
+  QTextCodec* codec_{nullptr};
+  QTextDecoder* decoder_{nullptr};
+  QTextEncoder* encoder_{nullptr};
+  QString unicodebuffer_;
+  qint64 unicodebuffer_bytes_{0};
+  const char* unicodebuffer_data_{nullptr};
+  static constexpr qint64 charbuffer_size_ = 1024;
+  char charbuffer_[charbuffer_size_];
+  char* charbuffer_data_{charbuffer_};
+  qint64 charbuffer_bytes_free_{charbuffer_size_};
+};
+
+} // namespace
index d020e30934e98307c7e393e3a7fcf0ec41b2fa0b..8d9b41e9d794052343c10296cb8f2755a733e67f 100644 (file)
@@ -49,4 +49,15 @@ public:
   explicit FatalMsg() : QDebug(QtCriticalMsg) {}
 };
 
+/*
+ * Kludge any used QTextStream modifiers into Qt namespace as they are in newer
+ * versions of Qt.  This makes source compatiblity easier.
+ */
+#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
+namespace Qt
+{
+  inline QTextStream& hex(QTextStream &s) { return ::hex(s); }
+  inline QTextStream& endl(QTextStream &s) { return ::endl(s); }
+}
+#endif
 #endif //  gpsbabel_logging_h_included
index 2a9adf5a7b26acc557c0a7f9411bfdbf855ebf4b..49c10ba88324bfe6e024a00b47f2da382cb80869 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2019 Robert Lipe, gpsbabel.org
+    Copyright (C) 2019-2021 Robert Lipe, gpsbabel.org
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
  */
 
-#include <QFile>               // for QFile
+
+#include <QtCore>            // for qint64, QT_VERSION, QT_VERSION_CHECK
+
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <QByteArrayView>    // for QByteArrayView
+#endif
+#include <QFile>             // for QFile
 #include <QFlags>            // for QFlags
-#include <QIODevice>         // for QIODevice, QIODevice::OpenMode, QIODevice::ReadOnly, QIODevice::WriteOnly
+#include <QIODevice>         // for QIODevice
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <QIODeviceBase>           // for QIODeviceBase::OpenMode
+#include <QStringConverter>  // for QStringConverter, QStringConverter::Utf8, QStringConverter::Encoding, QStringConverter::Utf16
+#endif
 
-#include "defs.h"              // for fatal, list_codecs
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <optional>          // for optional
+#endif
+
+#include "defs.h"            // for fatal, list_codecs
 #include "src/core/textstream.h"
-#include "src/core/file.h"     // for File
+#include "src/core/file.h"   // for File
 
 
 namespace gpsbabel
@@ -31,6 +45,7 @@ namespace gpsbabel
 
 void TextStream::open(const QString& fname, QIODevice::OpenMode mode, const char* module, const char* codec_name)
 {
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
   codec_ = QTextCodec::codecForName(codec_name);
   if (codec_ == nullptr) {
     list_codecs();
@@ -54,6 +69,54 @@ void TextStream::open(const QString& fname, QIODevice::OpenMode mode, const char
       setGenerateByteOrderMark(true);
     }
   }
+#else
+  std::optional<QStringConverter::Encoding> encoding = QStringConverter::encodingForName(codec_name);
+  bool use_stringconverter = encoding.has_value();
+
+  /* When reading autodetect unicode.
+   * The requested codec may not be supported by QStringConverter,
+   * but autodetection may switch to a converter that is.
+   */
+  if (!use_stringconverter && (mode & QFile::ReadOnly)) {
+    QFile file = QFile(fname);
+    file.open(mode);
+    char data[4];
+    qint64 bytesread = file.read(data, 4);
+    file.close();
+    encoding = QStringConverter::encodingForData(QByteArrayView(data, bytesread));
+    if (encoding.has_value()) {
+      use_stringconverter = true;
+    }
+  }
+
+  if (use_stringconverter) {
+    file_ = new gpsbabel::File(fname);
+    file_->open(mode);
+    setDevice(file_);
+    setEncoding(encoding.value());
+
+    if (mode & QFile::ReadOnly) {
+      if (encoding.value() == QStringConverter::Utf8) {
+        setAutoDetectUnicode(true);
+      }
+    }
+
+    if (mode & QFile::WriteOnly) {
+      // enable bom for all UTF codecs except UTF-8
+      if (encoding.value() != QStringConverter::Utf8) {
+        setGenerateByteOrderMark(true);
+      }
+    }
+  } else {
+    device_ = new gpsbabel::CodecDevice(fname, module, codec_name);
+    bool status = device_->open(mode);
+    if (!status) {
+      fatal("%s: device not open %d\n", module, status);
+    }
+    setDevice(device_);
+    setEncoding(QStringConverter::Utf16);
+  }
+#endif
 }
 
 void TextStream::close()
@@ -64,7 +127,15 @@ void TextStream::close()
     delete file_;
     file_ = nullptr;
   }
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
   codec_ = nullptr;
+#else
+  if (device_ != nullptr) {
+    device_->close();
+    delete device_;
+    device_ = nullptr;
+  }
+#endif
 }
 
 } // namespace
index a1f1c8fba01aa68ab5b97294de19fcf4404c40d2..a3f524b31f9f21a97ed06948face0bb07c6006ee 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2019 Robert Lipe, gpsbabel.org
+    Copyright (C) 2019-2021 Robert Lipe, gpsbabel.org
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #ifndef SRC_CORE_TEXTSTREAM_INCLUDED_H_
 #define SRC_CORE_TEXTSTREAM_INCLUDED_H_
 
-#include <QIODevice>           // for QIODevice, QIODevice::OpenMode
-#include <QString>             // for QString
-#include <QTextCodec>          // for QTextCodec
-#include <QTextStream>         // for QTextStream
+#include <QtGlobal>                // for QT_VERSION, QT_VERSION_CHECK
 
-#include "src/core/file.h"     // for File
+#include <QIODevice>               // for QIODevice
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <QIODeviceBase>           // for QIODeviceBase::OpenMode
+#endif
+#include <QString>                 // for QString
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
+#include <QTextCodec>              // for QTextCodec
+#endif
+#include <QTextStream>             // for QTextStream
+
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include "src/core/codecdevice.h"  // for CodecDevice
+#endif
+#include "src/core/file.h"         // for File
 
 
 namespace gpsbabel
@@ -38,7 +48,11 @@ public:
 
 private:
   gpsbabel::File* file_{nullptr};
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
   QTextCodec* codec_{nullptr};
+#else
+  gpsbabel::CodecDevice* device_{nullptr};
+#endif
 };
 
 } // namespace
index 30b45cd1f2d6d085e370d68b3c15f40f80d07977..235f55518686c824e32c95517b7c0911598b7291 100755 (executable)
@@ -34,7 +34,7 @@ make clean
 make -j 3
 make check
 
-export CLAZY_CHECKS=level0,level1,no-non-pod-global-static
+export CLAZY_CHECKS=level0,level1,no-non-pod-global-static,no-qstring-ref
 qmake -spec linux-clang "CONFIG+=debug" "QMAKE_CXX=clazy"
 make clean
 make -j 3 2>&1 | tee clazy.log
diff --git a/tools/build_qt6.sh b/tools/build_qt6.sh
new file mode 100755 (executable)
index 0000000..d413a71
--- /dev/null
@@ -0,0 +1,136 @@
+#!/bin/bash -ex
+
+if [ $# -ne 1 ]; then
+  echo "$0 version"
+  exit 1
+fi
+version=${1}
+
+sourcetype=git
+#flavor=debug
+flavor=release
+#flavor=debug-and-release
+
+buildroot=$(pwd)/qt-${version}-${flavor}-${sourcetype}
+mkdir "$buildroot"
+sourcedir=${buildroot}/source
+builddir=${buildroot}/build
+if [ "$(uname -s)" = "Darwin" ]; then
+  installdir=/Users/travis/Cache/Qt
+  compilerdir=clang_64
+  archive=qt-${version}-${flavor}-macos
+else
+  installdir=/home/travis/Cache/Qt
+  compilerdir=gcc_64
+  archive=qt-${version}-${flavor}-linux
+fi
+
+
+if [ -e "${sourcedir}" ]; then
+  echo "source directory \"${sourcedir}\" already exits."
+  exit 1
+fi
+if [ -e "${builddir}" ]; then
+  echo "build directory \"${builddir}\" already exits."
+  exit 1
+fi
+if [ -e "${installdir}" ]; then
+  echo "install directory \"${installdir}\" already exits."
+  exit 1
+fi
+
+if [ "${sourcetype}" == "git" ]; then
+  git clone git://code.qt.io/qt/qt5.git "${sourcedir}"
+else
+  mkdir -p "${sourcedir}"
+  versionmm=$(echo "${version}" | cut -d. -f1,2)
+  if [ ! -e "qt-everywhere-src-${version}.tar.xz" ]; then
+    wget -nv "https://download.qt.io/archive/qt/${versionmm}/${version}/single/qt-everywhere-src-${version}.tar.xz"
+  fi
+  tar -x --strip-components 1 --xz --directory "${sourcedir}" --file "qt-everywhere-src-${version}.tar.xz"
+fi
+
+cd "${sourcedir}"
+
+# exclude modules without some kind of LGPL license.
+# virtualkeyboard is pretty sticky, it gets deployed if it exists and you use Gui.
+excludes=( \
+qtcharts \
+qtdatavis3d \
+qtlottie \
+qtnetworkauth \
+qtquick3d \
+qtvirtualkeyboard \
+qtwebglplugin \
+)
+
+# also some modules we don't use
+excludes+=( \
+qtpurchasing \
+qtscript \
+qtxmlpatterns \
+qtactiveqt \
+qtcoap \
+qtopcua \
+qtmqtt \
+qtscxml \
+)
+
+# other modules we don't want
+# the tarballs don't include these modules, but git does
+excludes+=( \
+qtqa \
+qtscript \
+qtquickcontrols2 \
+qtwayland \
+qt3d \
+qtquicktimeline \
+qtdoc \
+)
+#qtwebengine \
+#qtwebchannel \
+
+# qtmultimedia requires:
+#qtimageformats \
+#qtshadertools \
+
+if [ "${sourcetype}" == "git" ]; then
+  if true; then
+    # tagged when released
+    git checkout "v${version}"
+  else
+    # branch, before tagged
+    git checkout "${version}"
+  fi
+  modules=essential,addon,qt5compat,-$(echo "${excludes[@]}" | sed 's/ /,-/g')
+  echo "$modules"
+  perl init-repository --module-subset="${modules}"
+else
+  for component in "${excludes[@]}"
+  do
+    /bin/rm -fr "${component}"
+  done
+fi
+skips=$(echo "${excludes[@]}" | sed 's/[^ ]* */-skip &/g')
+mkdir -p "${builddir}"
+cd "${builddir}"
+"${sourcedir}/configure" --prefix="${installdir}/${version}/${compilerdir}" -opensource -confirm-license -nomake examples -nomake tests "-${flavor}" "${features[@]}" ${skips}
+cmake --build . --parallel
+cmake --install .
+
+licenses=( \
+"${sourcedir}/LICENSE.FDL"  \
+"${sourcedir}/LICENSE.GPLv2" \
+"${sourcedir}/LICENSE.GPLv3" \
+"${sourcedir}/LICENSE.LGPLv21" \
+"${sourcedir}/LICENSE.LGPLv3" \
+"${sourcedir}/LICENSE.QT-LICENSE-AGREEMENT" \
+)
+
+mkdir "${installdir}/Licenses"
+for license in "${licenses[@]}"
+do
+  cp "${license}" "${installdir}/Licenses"
+done
+
+tar -c -C "$(dirname ${installdir})" --xz -f "${archive}.tar.xz" "$(basename ${installdir})"
diff --git a/tools/ci_install_qt.sh b/tools/ci_install_qt.sh
new file mode 100755 (executable)
index 0000000..f56dfc2
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/bash -e
+
+# install Qt
+#
+# Examples:
+# ci_install_qt.sh mac 6.2.0 clang_64 /tmp/Qt
+# ci_install_qt.sh windows 6.2.0 win64_msvc2019_64 /tmp/Qt
+# ci_install_qt.sh linux 6.2.0 gcc_64 /tmp/Qt
+
+host=$1
+version=$2
+arch=$3
+outdir=$4
+
+available=( $(aqt list-qt "$host" desktop --modules "$version" "$arch") )
+
+# remove commercial/GPLv3 modules, see https://doc-snapshots.qt.io/qt6-dev/qtmodules.html
+remove=( \
+debug_info \
+qtcharts \
+qtdatavis3d \
+qtlottie \
+qtnetworkauth \
+qtquick3d \
+qtquicktimeline \
+qtwebglplugin \
+qtshadertools \
+qtvirtualkeyboard \
+qtwaylandcompositor \
+)
+
+mods=()
+for a in "${available[@]}"
+do
+  skip=false
+  for r in "${remove[@]}"
+  do
+    if [ "$a" == "$r" ]; then
+      skip=true
+    fi
+  done
+  if [ $skip == false ]; then
+    mods+=( "$a" )
+  fi
+done
+echo Installing "${mods[@]}"
+aqt install-qt "$host" desktop "$version" "$arch" -O "$outdir" -m "${mods[@]}"
+
index db91d4a55d146bec3e5630d5287f244836771c60..f97a4a4ef5bb206e9c11320a3b2aa6ba6076870b 100755 (executable)
@@ -2,6 +2,8 @@
 #
 # this script is run on travis for the script stage of mac builds
 #
+function version_ge() { test "$(printf "%s\n%s" $1 $2 | sort -rV | head -n 1)" == "$1"; }
 
 QMAKE=${QMAKE:-qmake}
 LUPDATE=${LUPDATE:-lupdate}
@@ -14,13 +16,21 @@ VERSIONID=${VERSIONID:-$(date -ju -f %Y-%m-%dT%H:%M:%S%z $(git show -s --format=
 "$(cd "$(dirname "${BASH_SOURCE[0]}" )" && pwd)"/ci_tokens
 
 # build and test the CLI
-$QMAKE GPSBabel.pro
+if version_ge $($QMAKE -query QT_VERSION) 6.0.0; then
+  $QMAKE GPSBabel.pro QMAKE_APPLE_DEVICE_ARCHS="x86_64 arm64"
+else
+  $QMAKE GPSBabel.pro
+fi
 make -j 3
 make check
 
 # build the GUI
 pushd gui
-$QMAKE app.pro
+if version_ge $($QMAKE -query QT_VERSION) 6.0.0; then
+  $QMAKE app.pro QMAKE_APPLE_DEVICE_ARCHS="x86_64 arm64"
+else
+  $QMAKE app.pro
+fi
 make -j 3
 make package
 popd
diff --git a/util.cc b/util.cc
index 30ec114f101f12ef4788a960c804e0c62e0bdb90..7a4b4c27ef0be61cd12093830805e901942e75ce 100644 (file)
--- a/util.cc
+++ b/util.cc
@@ -1744,8 +1744,8 @@ list_codecs()
       maxlen = codec->name().size();
     }
   }
-  info << "Available Codecs:" << endl;
-  info << qSetFieldWidth(8) << "MIBenum" << qSetFieldWidth(maxlen+1) << "Name" << qSetFieldWidth(0) << "Aliases" << endl;
+  info << "Available Codecs:" << Qt::endl;
+  info << qSetFieldWidth(8) << "MIBenum" << qSetFieldWidth(maxlen+1) << "Name" << qSetFieldWidth(0) << "Aliases" << Qt::endl;
   for (auto mib : mibs) {
     auto* codec = QTextCodec::codecForMib(mib);
     info << qSetFieldWidth(8) << mib << qSetFieldWidth(maxlen+1) << codec->name() << qSetFieldWidth(0);
@@ -1759,7 +1759,7 @@ list_codecs()
       }
       info << alias;
     }
-    info << endl;
+    info << Qt::endl;
   }
 }
 
diff --git a/xcsv.cc b/xcsv.cc
index 5e413ea791b881b06f4b15c53c485f6bc3634cd6..4114018ee02dc2d4f30e3e6efcd433bdd006f9af 100644 (file)
--- a/xcsv.cc
+++ b/xcsv.cc
@@ -258,7 +258,11 @@ QDateTime
 XcsvFormat::yyyymmdd_to_time(const char* s)
 {
   QDate d = QDate::fromString(s, "yyyyMMdd");
+#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
   return QDateTime(d);
+#else
+  return d.startOfDay();
+#endif
 }